package com.cedar.util; 

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**  
 * @Author Bruce 
 * @Date 2012-9-30
 * @Comments 创建ORM映射文件（注意：如果文件已存在会覆盖原文件）
 */
public class MakeOrm {
	
	private static final String BASE_PATH = System.getProperty("user.dir");
	private static String POJO_PATH = "/src/com/cedar/model/";//表对应的的java实体类
	private static String XML_PATH = "/src/com/cedar/mapper/";//表与java实体类的映射文件
	
	private static final String dbName = "cedar_db";
	private static final String dbUser = "root";
	private static final String dbPwd = "";
	private static final String[] tbNames = {"client"};//表名
	
	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		DBUtil db = new DBUtil(dbName,dbUser,dbPwd);
		for (int i = 0; i < tbNames.length; i++) {
			LinkedHashMap<String,String> fields = db.getFieldsNamesAndType(tbNames[i]);
			createOrmFiles(tbNames[i], fields);
		}
		db.close();
		/*String setMethod = "\tpublic void setId({type} {name}) {\r\n\t\tthis.{name} = {name};\r\n\t}\r\n";//public void setId(long id) {
		System.out.println(setMethod.replaceAll("\\{name\\}", "逸馨"));*/
		System.out.println("\ndone! Please refresh project!");
	}

	private static void createOrmFiles(String tbName, LinkedHashMap<String,String> fieldsNamesAndType) throws IOException{
		String poName = formatName(tbName, true);
		Map<String,LinkedHashMap<String,String>> fieldsInfo = getFieldsInfo(fieldsNamesAndType);
		createXmlMapper(tbName, poName, fieldsInfo.get("tbAndOpFieldsName"));
		createPOJO(poName, fieldsInfo.get("opAndOpTypeName"));
	}
	
	private static final String IMPORT = "import {fullNameType};";
	private static final String PROPERTY = "\tprivate {type} {name};\r\n";
	private static final String GET_METHOD = "\tpublic {type} get{Name}() {\r\n\t\treturn {name};\r\n\t}\r\n";//public long getId() {
	private static final String SET_METHOD = "\tpublic void set{Name}({type} {name}) {\r\n\t\tthis.{name} = {name};\r\n\t}\r\n";//public void setId(long id) {
	/**
	 * 根据数据库表，生成javaBean
	 * @param tbName
	 * @param fields
	 * @param file
	 * @throws IOException
	 */
	private static void createPOJO(String poName, LinkedHashMap<String,String> fieldsNamesAndType)throws IOException{
		String fileName = BASE_PATH + POJO_PATH + poName + ".java";
		File file = new File(fileName);
		if(file.exists()){
			file.delete();
		}
		file.createNewFile();//如文件已存在将替换/覆盖
		FileWriterExt fw = new FileWriterExt(file);
		//fw.write("package com.diligreat.domain;");
		if(POJO_PATH.endsWith("/")){
			POJO_PATH = POJO_PATH.substring(0, POJO_PATH.length()-1);
		}
		//写入package
		fw.write("package " + POJO_PATH.replace("/src/", "").replace("/", ".") + ";");
		fw.write("");
		Set<String> imps = new HashSet<String>();//使用Set集合过滤重复import
		StringBuffer properties = new StringBuffer();//属性
		StringBuffer getAndSet = new StringBuffer();//getter and setter method
		for (Map.Entry<String, String> entry : fieldsNamesAndType.entrySet()) {
			String name = entry.getKey();
			String fullNameType = entry.getValue();//带package类名
			int idx = fullNameType.lastIndexOf(".");//通过“.”来判断是否为Java基本数据类型
			String type = fullNameType;
			if(idx != -1){
				type = fullNameType.substring(idx+1);
				imps.add(IMPORT.replace("{fullNameType}", fullNameType));
			}
			String nameUpper = formatName(name, true);
			properties.append(PROPERTY.replace("{type}", type).replace("{name}", name));
			
			getAndSet.append(GET_METHOD.replace("{type}", type).replace("{Name}", nameUpper).replace("{name}", name));
			getAndSet.append(SET_METHOD.replace("{Name}", nameUpper).replace("{type}", type).replaceAll("\\{name\\}", name));
		}
		//写入import
		for (String str : imps) {
			fw.write(str);
		}
		//写入properties
		fw.write("");
		fw.write("public class "+poName+" {");
		fw.write(properties.toString());
		//写入get、set方法
		fw.write(getAndSet.toString());
		fw.write("}");
		fw.close();
	}
	/** 数据库字段类型 => Java类型  （可根据需要修改）**/
	private static Map<String,String> fieldsTypeMap = new HashMap<String,String>();
	static{
		fieldsTypeMap.put("VARCHAR", "java.lang.String");
		fieldsTypeMap.put("CHAR", "java.lang.String");
		fieldsTypeMap.put("BLOB", "java.lang.byte[]");
		fieldsTypeMap.put("TEXT", "java.lang.String");
		fieldsTypeMap.put("BIT", "java.lang.Boolean");
		fieldsTypeMap.put("DECIMAL", "java.math.BigDecimal");
		fieldsTypeMap.put("DATE", "java.sql.Date");
		fieldsTypeMap.put("TIME", "java.sql.Time");
		fieldsTypeMap.put("DATETIME", "java.sql.Timestamp");
		fieldsTypeMap.put("TIMESTAMP", "java.sql.Timestamp");
		fieldsTypeMap.put("YEAR", "java.sql.Date");
		fieldsTypeMap.put("FLOAT", "java.lang.Float");
		//fieldsTypeMap.put("INTEGER", "int");
		fieldsTypeMap.put("INTEGER", "java.lang.Long");
		fieldsTypeMap.put("BIGINT", "java.math.BigInteger");
		fieldsTypeMap.put("TINYINT", "java.lang.Integer");
		fieldsTypeMap.put("SMALLINT", "java.lang.Integer");
		fieldsTypeMap.put("MEDIUMINT", "java.lang.Integer");
		fieldsTypeMap.put("DOUBLE", "java.lang.Double");
	}
	
	private static final String xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	private static final String xml_doc_type = "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"  \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">";
	private static final String xml_root = "<mapper namespace=\"{namespace}\">";
	private static final String xml_root_end = "</mapper>";
	private static final String resultMap = "\t<resultMap id=\"{id}\" type=\"{type}\">";
	private static final String resultMap_end = "\t</resultMap>";
	private static final String result = "\t\t<result property=\"{property}\" column=\"{column}\"/>";
	private static final String sql_insert = "\t<insert id=\"{id}\" parameterType=\"{parameterType}\" useGeneratedKeys=\"true\" keyProperty=\"id\">";
	private static final String sql_insert_end = "\t</insert>";
	private static final String insert_str = "\tINSERT INTO {tableName} ({fields})\r\n\t VALUES ({values})";
	/**
	 * 根据数据库表，生成xml映射文件
	 * @param xmlName(不带扩展名.xml)
	 * @param fields
	 * @throws IOException
	 */
	private static void createXmlMapper(String tbName, String poName, LinkedHashMap<String,String> tbAndOpFieldsName) throws IOException{
		String mapperName = poName + "Mapper";
		StringBuffer fieldsStr = new StringBuffer();
		StringBuffer valuesStr = new StringBuffer();
		String fileName = BASE_PATH + XML_PATH + mapperName + ".xml";
		File file = new File(fileName);
		if(file.exists()){
			file.delete();
		}
		file.createNewFile();//如文件已存在将替换/覆盖
		FileWriterExt fw = new FileWriterExt(file);
		fw.write(xml_header);
		fw.write(xml_doc_type);
		fw.write(xml_root.replace("{namespace}", XML_PATH.replace("/src/", "").replace("/", ".") + mapperName));
		fw.write(resultMap.replace("{id}", mapperName.toLowerCase().replace("mapper", "Result")).replace("{type}", poName));
		for (Map.Entry<String, String> entry : tbAndOpFieldsName.entrySet()) {
			fw.write(result.replace("{property}", entry.getValue()).replace("{column}", entry.getKey()));
			fieldsStr.append(entry.getKey());
			fieldsStr.append(",");
			valuesStr.append("#{");
			valuesStr.append(entry.getValue());
			valuesStr.append("},");
		}
		fieldsStr.deleteCharAt(fieldsStr.length()-1);
		valuesStr.deleteCharAt(valuesStr.length()-1);
		fw.write(resultMap_end);
		fw.write(sql_insert.replace("{id}", "insert"+poName).replace("{parameterType}", poName));
		fw.write(insert_str.replace("{tableName}", tbName).replace("{fields}", fieldsStr.toString()).replace("{values}", valuesStr.toString()));
		fw.write(sql_insert_end);
		fw.writeNoLineFeed(xml_root_end);
		fw.close();
	}
	/**
	 * 生成字段名。将格式为“test_name_ok”的字段名变为“testNameOk”
	 * @param fieldName 字段名
	 * @param firestUpper 开头字母是否大写
	 * @return
	 */
	private static String formatName(String fieldName, boolean firstUpper){
		int idx = -1;
		while ((idx = fieldName.indexOf("_")) != -1 && idx != (fieldName.length()-1)){
			int idxEnd = idx+2;
			String oldChar = fieldName.substring(idx, idxEnd);
			String newChar = fieldName.substring(idx+1, idxEnd).toUpperCase();
			fieldName = fieldName.replaceFirst(oldChar, newChar);
		}
		if(firstUpper == true){
			fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, fieldName.length());
		}
		return fieldName;
	}
	/**
	 * 把“字段名”-“DB字段类型” 的map 转成 “字段名”-“PO属性名” LinkedHashMap<tbFieldName,opFieldName>
	 * 把“字段名”-“DB字段类型” 的map 转成 “PO属性名”-“Java类型” LinkedHashMap<opFieldName,javaTypeName>
	 * @param fields
	 * @return
	 */
	private static Map<String,LinkedHashMap<String,String>> getFieldsInfo(LinkedHashMap<String,String> fields){
		//把“字段名”-“DB字段类型” 的map 转成 “字段名”-“PO属性名” LinkedHashMap<tbFieldName,opFieldName>
		LinkedHashMap<String,String> tbAndOpFieldsName = new LinkedHashMap<String,String>();
		//把“字段名”-“DB字段类型” 的map 转成 “PO属性名”-“Java类型” LinkedHashMap<tbFieldName,opFieldName>
		LinkedHashMap<String,String> opAndOpTypeName = new LinkedHashMap<String,String>();
		for (Map.Entry<String, String> entry : fields.entrySet()) {   
            //System.out.println("key= " + entry.getKey() + "  and  value= "  + entry.getValue());   
			String opFieldName = formatName(entry.getKey(), false);
			tbAndOpFieldsName.put(entry.getKey(), opFieldName);
			opAndOpTypeName.put(opFieldName, fieldsTypeMap.get(entry.getValue().toUpperCase()));
        } 
		Map<String, LinkedHashMap<String,String>> map = new HashMap<String, LinkedHashMap<String,String>>();
		map.put("tbAndOpFieldsName", tbAndOpFieldsName);
		map.put("opAndOpTypeName", opAndOpTypeName);
		return map;
	}
}

class FileWriterExt extends FileWriter {
	private static final String LINE_FEED = "\r\n";
	
	public FileWriterExt(File file) throws IOException {
		super(file,true);
	}
	
	public FileWriterExt(String file) throws IOException {
		super(file, true);
	}
	@Override
	public void write(String str) throws IOException {
		super.write(str + LINE_FEED);
	}
	public void writeNoLineFeed(String str) throws IOException {
		super.write(str);
	}
}